package com.csu.csumall.security;

import com.csu.csumall.common.UniformResponse;
import com.csu.csumall.entity.User;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.StandardPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;

import static com.csu.csumall.common.Constant.ORIGIN;
import static org.springframework.security.config.Customizer.withDefaults;


/**
 * 参考：
 * https://www.cnblogs.com/guos/archive/2019/10/02/11617243.html
 * https://www.jianshu.com/p/650a497b3a40
 */
@Slf4j
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final UserDetailsService userDetailsService;

    @Autowired
    public SecurityConfig(@Qualifier("userDetailsServiceImp") UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(encoder());
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 参考 https://www.jianshu.com/p/650a497b3a40
        http.authorizeRequests()

            .and()
                .cors().configurationSource(corsConfigurationSource())

            .and()
                .formLogin()
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/api/user/login")
                .successHandler(new LoginSuccessHandler()) // 登录成功
                .failureHandler(new LoginFailureHandler()) // 登录失败

            .and()
                .logout()
                .logoutUrl("/user/logout")
                .logoutSuccessHandler(new MyLogoutSuccessHandler())
                .permitAll()

            .and()
                .csrf()
                .disable();
    }

    private class MyLogoutSuccessHandler implements LogoutSuccessHandler {
        @Override
        public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
            log.info("退出登录");
            httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
            httpServletResponse.setHeader("Access-Control-Allow-Origin", ORIGIN);
            ObjectMapper objectMapper = new ObjectMapper();
            String s = objectMapper.writeValueAsString(UniformResponse.createForSuccess());
            httpServletResponse.getWriter().write(s);
        }
    }

    private class LoginSuccessHandler implements AuthenticationSuccessHandler {

        @Override
        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
            httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
            httpServletResponse.setHeader("Access-Control-Allow-Origin", ORIGIN);
            User user = (User) authentication.getPrincipal();
            ObjectMapper objectMapper = new ObjectMapper();
            String s = objectMapper.writeValueAsString((UniformResponse.createForSuccess(user)));
            httpServletResponse.getWriter().write(s);
        }
    }

    private class LoginFailureHandler implements AuthenticationFailureHandler {

        @Override
        public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
            httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
            httpServletResponse.setHeader("Access-Control-Allow-Origin", ORIGIN);
            String s = null;
            ObjectMapper objectMapper = new ObjectMapper();
            if (e instanceof UsernameNotFoundException || e instanceof BadCredentialsException) {
                s = objectMapper.writeValueAsString(UniformResponse.createForError("用户名或密码错误"));
            } else if (e instanceof DisabledException) {
                s = objectMapper.writeValueAsString(UniformResponse.createForError("账户被禁用"));
            } else {
                s = objectMapper.writeValueAsString(UniformResponse.createForError("登录失败"));
            }
            httpServletResponse.getWriter().write(s);
        }
    }

    @Bean
    public PasswordEncoder encoder() {
        return new StandardPasswordEncoder("53r3tc");
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("http://43.142.37.148:81"));
        configuration.setAllowCredentials(true);
        configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Headers", "Origin", "X-Requested-With", "Content-Type", "Accept"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST", "PATCH", "DELETE"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
